#include "common.h"

typedef struct Trace{  //trace数据结构
    int flag;       // R/W标识  R=0   W=1
    long long blockID;   // trace对应的blockID
}Trace;

int client2worker_sockfd[NodeNumTotal-1]={-1};//client作为客户端与worker之间的通信
int client2master_sockfd=-1;//client作为客户端与master之间的通信
Node* nodes=NULL;//节点信息
Rack* racks=NULL;//机架信息
Trace* traces=NULL;//trace数组
long traceNum;//trace条数

FILE *responseTime_file=NULL;

long init_trace(Trace *traces, char *traceFile);
int init_client();
int playTrace();
int exeWriteTrace(long requestID,long long blockID);
int exeReadTrace(long requestID,long long blockID); 
int master2client_recv_Resp4Read(Master2ClientResponse4Read* master2ClientResponse4Read);
int master2client_recv_Resp4Write(Master2ClientResponse4Write* master2ClientResponse4Write);
void client2worker_send(Client2WorkerRequest* request);
void client2master_sendReq(Client2MasterRequest *request);
void destory_client();
int main()
{
	int ret;
	ret = init_client();
	responseTime_file=fopen("responseTime.txt","a+");
	if (ret==ERR){
		printf("fail to init worker\n");
		destory_client();
		return 0;
	}

	ret = playTrace();
	if (ret==ERR)
	{
		printf("fail to execute the full request\n");
	}else{
		printf("has executed the full request successfully\n");
	}
	fclose(responseTime_file);
	destory_client();
	return 0;
}

int init_client(){
	printf("******************************init_client begin ................\n");
	if (set_server_limit() <0) {
      	printf("init_master()====set_server_limit error\n");
		return ERR;
    }

    nodes=read_config_Node(CONFIG_NODE);
	printf("read_config_Node end............................\n");
	racks=read_config_Rack(CONFIG_RACK);
	printf("read_config_Rack end............................\n");


	//向master发送connect
	client2master_sockfd = connect_try(nodes[NodeNumTotal-1].ip,Client2MasterPort);
	if (client2master_sockfd!=-1)
	{
		printf("client2master_sockfd successed and client2master_sockfd=%d \n",client2master_sockfd);
	}else{
		printf("client2master_sockfd is error and client2master_sockfd=%d \n",client2master_sockfd);
 		return ERR;
	}
	//向worker发送connect
	for (int i = 0; i < NodeNumTotal-1; ++i)
	{
		client2worker_sockfd[i]=connect_try(nodes[i].ip,Client2WorkerPort);
		if (client2worker_sockfd[i]!=-1)
		{
			printf("client2worker_sockfd successed and client2worker_sockfd[%d]=%d \n",i,client2master_sockfd);
		}else{
			printf("client2worker_sockfd is error and client2worker_sockfd[%d]=%d \n",i,client2master_sockfd);
	 		return ERR;
		}
	}


	traces = (Trace*)malloc(sizeof(Trace)*Max_Trace);
	traceNum = init_trace(traces,TRACE);

	printf("client_init() end===========================\n");
  	return 0;

}

void destory_client(){
	if (nodes!=NULL) free(nodes);
	if (racks!=NULL) free(racks);
	if (traces!=NULL) free(traces);

	if (client2master_sockfd!=-1) close(client2master_sockfd);
	for (int i = 0; i < NodeNumTotal-1; ++i)
	{
		if (client2worker_sockfd[i]!=-1) close(client2worker_sockfd[i]);
	}

}


/**
 * [init_trace description]     trace  初始化
 * @param  traces    [description]  
 * @param  traceFile [description]
 * @return           [description]
 */
long init_trace(Trace *traces, char *traceFile){
	//定义临时变量
  char strbuf[512]={'\0'};
  char flag_tmp;
  long long blockID_tmp;

  long trace_nums=0;  //总的trace数目

  FILE *fp;
  if(!(fp=fopen(traceFile,"r"))){
    printf("打开文件失败 \n");
    return -1;
  }

 //文件处理
 for(int i=0;i<Max_Trace && !feof(fp);i++){
 	while(fgets(strbuf,sizeof(strbuf),fp) && strbuf[0]=='#');
	sscanf(strbuf,"%c%lld",&flag_tmp,&blockID_tmp);
	traces[i].blockID=blockID_tmp;
	if (flag_tmp=='W')
	{
		traces[i].flag= 1;
	}else{
		traces[i].flag= 0;
	}
	trace_nums++;
 }
    
    //trace输出测试
    // int i;
    // for(i=0;i<trace_nums;i++){
    //     printf("traces[%d]: blockID=%lld, flag=%d \n",i,traces[i].blockID,traces[i].flag);
    // }

    printf("Total record is %ld. \n",trace_nums);
    printf("############################################################################\n");
    fclose(fp);
    printf("Init trace successfully\n");
    return trace_nums;
}

//playtrace
//maybe many clients many thread many playtrace together
int playTrace(){
	printf("playTrace()========client start paly trace and  send request \n");
	long index;

	struct timeval stime,etime;
  	double responseTime;

	for (index = 0; index < traceNum; ++index)
	{
		gettimeofday(&stime,NULL);
		printf("playTrace()=======index=%ld \n",index );
		long blockID = traces[index].blockID;   //逻辑快号
		int flag = traces[index].flag;//W/R操作

		if (flag==0)
		{
			//read操作
			if(exeReadTrace(index,blockID)==-1){
				printf("Trace[%ld] read operation is error\n",index);
				return ERR;
			}else{
				printf("Trace[%ld] read operation successed\n",index);
			}
		}else if (flag==1)
		{
			//write操作
			if(exeWriteTrace(index,blockID)==-1){
				printf("Trace[%ld] write operation is error\n",index);
				return ERR;
			}else{
				printf("Trace[%ld] write operation successed\n",index);
			}
		}else{
			printf("Trace[%ld] no such operation\n",index);
			return ERR;
		}
		gettimeofday(&etime,NULL);
		responseTime=(etime.tv_sec-stime.tv_sec)+((double)(etime.tv_usec-stime.tv_usec)/1000000);
		printf("reqindex=%ld,flag=%d,responseTime=%f\n",index,flag,responseTime);
		fprintf(responseTime_file, "%d %lf\r\n",flag,responseTime);
	}

	return 0;
}

void client2master_sendReq(Client2MasterRequest *request){
	size_t needSend=sizeof(Client2MasterRequest);
	char *sendBuf=(char *)malloc(needSend);
	memcpy(sendBuf,request,needSend); //序列化
	send_bytes(client2master_sockfd,sendBuf,needSend); //Client发送请求至worker
	free(sendBuf);
}

int master2client_recv_Resp4Read(Master2ClientResponse4Read* master2ClientResponse4Read){
	size_t needRecv =sizeof(Master2ClientResponse4Read);
	char *recvBuf=(char *)malloc(needRecv*sizeof(char));
	int len = recv_bytes(client2master_sockfd,recvBuf,needRecv);
	memcpy(master2ClientResponse4Read,recvBuf,needRecv);  //反序列化
	free(recvBuf);
	return len;
}

int master2client_recv_Resp4Write(Master2ClientResponse4Write* master2ClientResponse4Write){
	size_t needRecv =sizeof(Master2ClientResponse4Write);
	char *recvBuf=(char *)malloc(needRecv*sizeof(char));
	int len = recv_bytes(client2master_sockfd,recvBuf,needRecv);
	memcpy(master2ClientResponse4Write,recvBuf,needRecv);  //反序列化
	free(recvBuf);
	return len;
}

int exeReadTrace(long requestID,long long blockID){
	//先查询块是否存在
	//如果存在,找到其主从副本的meta
	//选取负载较低的rack或者node作为访问目标
	//发送出request
	//接受block数据
	//接受requestID
	//修改元数据 主要是一些访问量的属性修改

	Client2MasterRequest* client2MasterRequest = (Client2MasterRequest*)malloc(sizeof(Client2MasterRequest));
	client2MasterRequest->requestID=requestID;
	client2MasterRequest->flag=0;
	client2MasterRequest->blockID=blockID;
	//向master发送request 获得访问的nodeid
	client2master_sendReq(client2MasterRequest);
	free(client2MasterRequest);
	printf("client2master_send has done\n");

	//接受来自master的回复
	Master2ClientResponse4Read* master2ClientResponse4Read = (Master2ClientResponse4Read*)malloc(sizeof(Master2ClientResponse4Read));
	int len = master2client_recv_Resp4Read(master2ClientResponse4Read);
	if (len<=0)
	{
		printf("recv the incorrect response from master len:%d\n",len);
		free(master2ClientResponse4Read);
		return ERR;
	}
	if (master2ClientResponse4Read->responseID!=requestID)
	{
		printf("recv the incorrect response from master requestID:%ld\n",master2ClientResponse4Read->responseID);
		free(master2ClientResponse4Read);
		return ERR;
	}

	if (master2ClientResponse4Read->nodeID1<0)
	{
		printf("can not find the block\n");
		free(master2ClientResponse4Read);
		return 0;
	}

	//
	//创建Client2WorkerRequest 给制定nodeid发送
	Client2WorkerRequest* client2WorkerRequest = (Client2WorkerRequest*)malloc(sizeof(Client2WorkerRequest));
	client2WorkerRequest->requestID=requestID;
	client2WorkerRequest->blockID=blockID;
	client2WorkerRequest->flag=0;
	client2WorkerRequest->nodeID = master2ClientResponse4Read->nodeID1;
	free(master2ClientResponse4Read);
	//向制定的nodeid发送request，执行trace
	client2worker_send(client2WorkerRequest);
	printf("request[%ld] read operation request has sent to nodeID[%d]\n",requestID,client2WorkerRequest->nodeID);

	char* recvBlock = (char*)malloc(sizeof(char)*Block_Size);
	recv_bytes(client2worker_sockfd[client2WorkerRequest->nodeID],recvBlock,Block_Size);
	printf("request[%ld]: client has received the block from worker\n",requestID);

	long requestID4Check;
	recv_bytes(client2worker_sockfd[client2WorkerRequest->nodeID],(char*)&requestID4Check,sizeof(long));
	if (requestID4Check!=requestID) return ERR;
	printf("request[%ld]:client has received the requestID from worker\n", requestID);

	printf("request[%ld]:has done\n", requestID);
	free(recvBlock);
	free(client2WorkerRequest);
	return 0;
}

void client2worker_send(Client2WorkerRequest* request){
	size_t needSend=sizeof(Client2WorkerRequest);
	char* sendBuf=(char*)malloc(sizeof(char)*needSend);
	memcpy(sendBuf,request,needSend); //序列化
	send_bytes(client2worker_sockfd[request->nodeID],sendBuf,needSend); //Client发送请求至worker
	if(sendBuf!=NULL) free(sendBuf);
}

int exeWriteTrace(long requestID,long long blockID){
	//先检查该块是否已经存在
	//1.该块已经存在 update操作
	//获得主从副本的meta信息 分别更新数据块
	//2.该块不存在 insert操作
	//根据数据块放置策略，选择rack，采用随机策略选择node，然后分别放置主从副本

	Client2MasterRequest* client2MasterRequest = (Client2MasterRequest*)malloc(sizeof(Client2MasterRequest));
	client2MasterRequest->requestID=requestID;
	client2MasterRequest->flag=1;
	client2MasterRequest->blockID=blockID;
	//向master发送request 获得访问的nodeid
	client2master_sendReq(client2MasterRequest);
	free(client2MasterRequest);
	printf("client2master_send has done\n");

	//接受来自master的回复
	Master2ClientResponse4Write* master2ClientResponse4Write = (Master2ClientResponse4Write*)malloc(sizeof(Master2ClientResponse4Write));
	int len = master2client_recv_Resp4Write(master2ClientResponse4Write);
	if (len<=0)
	{
		printf("recv the incorrect response from master len:%d\n",len);
		free(master2ClientResponse4Write);
		return ERR;
	}
	if (master2ClientResponse4Write->responseID!=requestID)
	{
		printf("recv the incorrect response from master requestID:%ld\n",master2ClientResponse4Write->responseID);
		free(master2ClientResponse4Write);
		return ERR;
	}

	if (master2ClientResponse4Write->nodeID1<0)
	{
		printf("can not find the suitable position\n");
		free(master2ClientResponse4Write);
		return 0;
	}

	//设置发往worker的request
	Client2WorkerRequest* request4Primary =(Client2WorkerRequest*)malloc(sizeof(Client2WorkerRequest));
	Client2WorkerRequest* request4Secondary =(Client2WorkerRequest*)malloc(sizeof(Client2WorkerRequest));

	request4Primary->requestID=requestID;
	request4Primary->blockID = blockID;
	request4Primary->flag = 1;
	request4Primary->nodeID = master2ClientResponse4Write->nodeID1;
	request4Primary->isPrimary=1;

	request4Secondary->requestID=requestID;
	request4Secondary->blockID = blockID;
	request4Secondary->flag = 1;
	request4Secondary->nodeID = master2ClientResponse4Write->nodeID2;
	request4Secondary->isPrimary=0;

	free(master2ClientResponse4Write);

	char* sendBuf = (char*)malloc(sizeof(char)*Block_Size);
	memset(sendBuf,'a',sizeof(char)*Block_Size);
	//发送request
	client2worker_send(request4Primary);
	send_bytes(client2worker_sockfd[request4Primary->nodeID],sendBuf,Block_Size);
	printf("request[%ld] primary  write operation request has sent to nodeID[%d]\n",requestID,request4Primary->nodeID);

	client2worker_send(request4Secondary);
	send_bytes(client2worker_sockfd[request4Secondary->nodeID],sendBuf,Block_Size);
	printf("request[%ld] secondary write operation request has sent to nodeID[%d]\n",requestID,request4Secondary->nodeID);
	
	//master接受requestID 判断是否一致？

	long recvRequestID4Primary;
	recv_bytes(client2worker_sockfd[request4Primary->nodeID],(char*)&recvRequestID4Primary,sizeof(long));
	if (recvRequestID4Primary!=requestID) return ERR;
	printf("recv requestID from worker recvRequestID4Primary:%ld check successfull\n",recvRequestID4Primary);

	long recvRequestID4Secondary;
	recv_bytes(client2worker_sockfd[request4Secondary->nodeID],(char*)&recvRequestID4Secondary,sizeof(long));
	if (recvRequestID4Secondary!=requestID) return ERR;
	printf("recv requestID from worker recvRequestID4Secondary:%ld check successfully\n",recvRequestID4Secondary);

	printf("request[%ld]:has done\n", requestID);
	if (sendBuf!=NULL) free(sendBuf);
	if (request4Primary!=NULL) free(request4Primary);
	if (request4Secondary!=NULL) free(request4Secondary);
	return 0;	
}